package netty.builder;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

public class NettyClientBuilder extends NettyBuilderBase<NettyClientBuilder> {
    private static final Logger LOG = LoggerFactory.getLogger(NettyClientBuilder.class);
    private EventLoopGroup loopGroup;

    public NettyClientBuilder() {
        // 设定默认值，后续相同设置参数会覆盖此值
        options.put(ChannelOption.SO_KEEPALIVE, true);
        options.put(ChannelOption.TCP_NODELAY, true);
    }

    @SuppressWarnings("unchecked")
    public Bootstrap build() {
        Assert.isTrue(channelInitializer != null || pipelineHandlers.size() > 0, "请指定channelInitializer或pipelineHandlers");
        if (loopGroup == null) {
            loopGroup = OS.defaultLoopGroup();
        }
        if (channelClass == null) {
            channelClass = OS.getDefaultClientChannelClass();
        }

        Bootstrap bs = new Bootstrap();
        bs.group(loopGroup).channel(channelClass).handler(channelInitializer != null ? channelInitializer : defaultChildHandler);
        options.forEach(bs::option);
        return bs;
    }

    public ChannelFuture buildAndConnect(String host, int port) {
        try {
            ChannelFuture f = build().connect(host, port).sync();
            LOG.debug("连接远程主机成功：" + host + ":" + port);
            return f;
        } catch (InterruptedException e) {
            throw new RuntimeException("连接远程主机失败：" + host + ":" + port, e);
        }
    }

    public ChannelFuture buildAndConnectAsync(String host, int port) {
        ChannelFuture f = build().connect(host, port);
        f.addListener(future -> {
            if (future.isSuccess()) {
                LOG.debug("连接远程主机成功：" + host + ":" + port);
            } else {
                throw new RuntimeException("连接远程主机失败：" + host + ":" + port);
            }
        });
        return f;
    }

    /**
     * 可选配置。指定EventLoopGroup时，注意channelClass与EventLoopGroup匹配。
     * @param group 指定的LoopGroup在channel关闭时需要自行清理
     * @return
     */
    public NettyClientBuilder loopGroup(EventLoopGroup group) {
        Assert.notNull(group, "EventGroup不能为null");
        this.loopGroup = group;
        return this;
    }
}
